/**
 * QUANSHI.com Inc.
 * Copyright (c) 2016-2017 All Rights Reserved.
 */
package com.quanshi.scheduler.job.biz.quartz.bean;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;

import com.quanshi.scheduler.core.base.RegistryConfig;
import com.quanshi.scheduler.core.base.Ret;
import com.quanshi.scheduler.core.biz.model.TriggerParam;
import com.quanshi.scheduler.core.enums.ExecutorBlockStrategyEnum;
import com.quanshi.scheduler.job.biz.enums.ExecutorFailStrategyEnum;
import com.quanshi.scheduler.job.biz.enums.ExecutorRouteStrategyEnum;
import com.quanshi.scheduler.job.biz.quartz.JobDynamicScheduler;
import com.quanshi.scheduler.job.biz.quartz.monitor.JobFailMonitor;
import com.quanshi.scheduler.job.biz.quartz.monitor.JobRegistryMonitor;
import com.quanshi.scheduler.job.dal.entity.TriggerGroup;
import com.quanshi.scheduler.job.dal.entity.TriggerInfo;
import com.quanshi.scheduler.job.dal.entity.TriggerLog;

/**
 * 
 * @author yanxiang.huang 2017-07-10 15:59:33
 */
public class RemoteHttpJobBean extends QuartzJobBean
{
    
    private static Logger logger = LoggerFactory.getLogger(RemoteHttpJobBean.class);

    @Override
    protected void executeInternal( JobExecutionContext context ) throws JobExecutionException
    {
        // load job
        JobKey jobKey = context.getTrigger().getJobKey();
        Long jobId = Long.valueOf(jobKey.getName());
        TriggerInfo jobInfo = JobDynamicScheduler.triggerInfoService.get(jobId);

        // log part-1
        TriggerLog jobLog = new TriggerLog();
        jobLog.setJobGroup(jobInfo.getJobGroup());
        jobLog.setJobId(jobInfo.getId());
        JobDynamicScheduler.triggerLogService.save(jobLog);
        logger.debug("job trigger start, logId:{}.", jobLog.getId());

        // log part-2 param
        jobLog.setGlueType(jobInfo.getGlueType());
        jobLog.setExecutorHandler(jobInfo.getExecutorHandler());
        jobLog.setExecutorParam(jobInfo.getExecutorParam());
        jobLog.setTriggerTime(new Date());

        // trigger request
        TriggerParam triggerParam = new TriggerParam();
        triggerParam.setJobId(jobInfo.getId());
        triggerParam.setExecutorHandler(jobInfo.getExecutorHandler());
        triggerParam.setExecutorParams(jobInfo.getExecutorParam());
        triggerParam.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
        triggerParam.setGlueType(jobInfo.getGlueType());
        triggerParam.setGlueSource(jobInfo.getGlueSource());
        triggerParam.setGlueUpdatetime(jobInfo.getGlueUpdatetime().getTime());
        triggerParam.setLogId(jobLog.getId());
        triggerParam.setLogDateTim(jobLog.getTriggerTime().getTime());

        // do trigger
        Ret<String> triggerResult = doTrigger(triggerParam, jobInfo, jobLog);

        // fail retry
        if (triggerResult.getCode() == Ret.FAIL_CODE &&
                ExecutorFailStrategyEnum.match(jobInfo.getExecutorFailStrategy(), null) == ExecutorFailStrategyEnum.FAIL_RETRY) {
            Ret<String> retryTriggerResult = doTrigger(triggerParam, jobInfo, jobLog);

            triggerResult.setCode(retryTriggerResult.getCode());
            triggerResult.setMsg(triggerResult.getMsg() + "<br><br><span style=\"color:#F39C12;\" >失败重试</span><br><br>" +retryTriggerResult.getMsg());
        }

        // log part-3
        jobLog.setTriggerCode(triggerResult.getCode());
        jobLog.setTriggerMsg(triggerResult.getMsg());
        JobDynamicScheduler.triggerLogService.update(jobLog);

        // monitor triger
        JobFailMonitor.monitor(jobLog.getId());
        logger.debug("job trigger end, logId:{}.", jobLog.getId());
    }

    private Ret<String> doTrigger(TriggerParam triggerParam, TriggerInfo jobInfo, TriggerLog jobLog){
        StringBuffer triggerSb = new StringBuffer();

        // exerutor address list
        ArrayList<String> addressList = null;
        TriggerGroup group = JobDynamicScheduler.triggerGroupService.get(jobInfo.getJobGroup());
        if (group.getAddressType() == 0) {
            triggerSb.append("注册方式：自动注册");
            addressList = (ArrayList<String>) JobRegistryMonitor.discover(RegistryConfig.RegistType.EXECUTOR.name(), group.getAppName());
        } else {
            triggerSb.append("注册方式：手动录入");
            if (StringUtils.isNotBlank(group.getAddressList())) {
                addressList = new ArrayList<String>(Arrays.asList(group.getAddressList().split(",")));
            }
        }
        triggerSb.append("<br>阻塞处理策略：").append(ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), ExecutorBlockStrategyEnum.SERIAL_EXECUTION).getTitle());
        triggerSb.append("<br>失败处理策略：").append(ExecutorFailStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), ExecutorFailStrategyEnum.FAIL_ALARM).getTitle());
        triggerSb.append("<br>地址列表：").append(addressList != null ? addressList.toString() : "");
        if (CollectionUtils.isEmpty(addressList)) {
            triggerSb.append("<br>----------------------<br>").append("调度失败：").append("执行器地址为空");
            return new Ret<String>(Ret.FAIL_CODE, triggerSb.toString());
        }

        // executor route strategy
        ExecutorRouteStrategyEnum executorRouteStrategyEnum = ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null);
        if (executorRouteStrategyEnum == null) {
            triggerSb.append("<br>----------------------<br>").append("调度失败：").append("执行器路由策略为空");
            return new Ret<String>(Ret.FAIL_CODE, triggerSb.toString());
        }
        triggerSb.append("<br>路由策略：").append(executorRouteStrategyEnum.name() + "-" + executorRouteStrategyEnum.getTitle());

        // route run / trigger remote executor
        Ret<String> routeRunResult = executorRouteStrategyEnum.getRouter().routeRun(triggerParam, addressList, jobLog);
        triggerSb.append("<br>----------------------<br>").append(routeRunResult.getMsg());
        return new Ret<String>(routeRunResult.getCode(), triggerSb.toString());

    }
}
